﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using ProceduralGolf.generation;

namespace ProceduralGolf.terrain
{
    class Triangle
    {
        public int Index { get { return index; } }

        int index;

        public int X1 { get { return x1; } }
        public int Y1 { get { return y1; } }
        public int X2 { get { return x2; } }
        public int Y2 { get { return y2; } }
        public int X3 { get { return x3; } }
        public int Y3 { get { return y3; } }

        public Vector2 V1 { get { return new Vector2(X1, Y1); } }
        public Vector2 V2 { get { return new Vector2(X2, Y2); } }
        public Vector2 V3 { get { return new Vector2(X3, Y3); } }

        int x1, y1; // coordinates of left vertex
        int x2, y2; // coordinates of center vertex
        int x3, y3; // coordinates of right vertex

        public Triangle(int index, int x1, int y1, int x2, int y2, int x3, int y3)
        {
            this.index = index;

            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;
            this.x3 = x3;
            this.y3 = y3;
        }

        public Triangle LeftChild()
        {
            if (IsLeaf())
                return null;

            int x = (x1 + x3) >> 1;
            int y = (y1 + y3) >> 1;
            return new Triangle(index * 2 + 1, x2, y2, x, y, x1, y1);
        }

        public Triangle RightChild()
        {
            if (IsLeaf())
                return null;

            int x = (x1 + x3) >> 1;
            int y = (y1 + y3) >> 1;
            return new Triangle(index * 2 + 2, x3, y3, x, y, x2, y2);
        }

        public bool IsLeaf()
        {
            return Math.Abs(x1 - x3) == 1 || Math.Abs(y1 - y3) == 1;
        }

        public float Variance(Heightmap heightmap)
        {
            float z1 = heightmap.getHeight(x1, y1);
            float z3 = heightmap.getHeight(x3, y3);

            int x = (x1 + x3) >> 1;
            int y = (y1 + y3) >> 1;
            float z = heightmap.getHeight(x, y);

            return Math.Abs(z - (z1 + z3) / 2);
        }

        public float GetHeight(int x, int y, Heightmap heightmap)
        {
            Vector2 p = new Vector2(x, y);
            if (InTriangle(p))
            {
                Vector3 v1 = new Vector3(X1, heightmap.getHeight(X1, Y1), Y1);
                Vector3 v2 = new Vector3(X2, heightmap.getHeight(X2, Y2), Y2);
                Vector3 v3 = new Vector3(X3, heightmap.getHeight(X3, Y3), Y3);

                Vector3 n = Vector3.Cross(Vector3.Subtract(v2, v1), Vector3.Subtract(v2, v3));
                float d = n.X * v1.X + n.Y * v1.Y + n.Z * v1.Z;
                // equation of plane : n.X x + n.Y y + n.Z z + d = 0
                // (n.X x + n.Z z + d)/n.Y = y
                return -(n.X * x + n.Z * y - n.X * v1.X - n.Y * v1.Y - n.Z * v1.Z) / n.Y;
            }
            return 0;
        }

        public bool InTriangle(int x, int y)
        {
            return InTriangle(new Vector2(x, y));
        }

        public bool InTriangle(Vector2 p)
        {
            if (Sign(p, V1, V2) < 0.0f)
                return false;
            if (Sign(p, V2, V3) < 0.0f)
                return false;
            if (Sign(p, V3, V1) < 0.0f)
                return false;

            return true;
        }

        float Sign(Vector2 v1, Vector2 v2, Vector2 v3)
        {
            return (v1.X - v3.X) * (v2.Y - v3.Y) - (v2.X - v3.X) * (v1.Y - v3.Y);
        }



        public GolfCourseLayout.GolfCourseLayer? Layer(GolfCourseLayout.GolfCourseLayer[,] layermap)
        {
            if (IsLeaf())
            {
                int x = (x1 + x2 + x3) / 3;
                int y = (y1 + y2 + y3) / 3;
                return layermap[x, y];
            }
            else
            {
                return null;
            }
        }
    }
}
